# -*- coding:utf-8 -*-
from win32com.client import Dispatch
import pythoncom
import json
import pywintypes

from .win_control.control import Control
from .win_control.window import Window
from .win_control.text import Text
from .win_control.button import Button
from .win_control.context_menu import ContextMenu
from .win_control.tool_bar import ToolBar
from .win_control.unknown import Unknown
from .win_control.combo_box import ComboBox
from .win_control.list import List
from .win_control.list_item import ListItem
from .win_control.scroll_bar import ScrollBar
from .win_control.indicator import Indicator
from .win_control.tree import Tree
from .win_control.tree_item import TreeItem
from .win_control.checkbox import CheckBox
from .win_control.radio_button import RadioButton
from .win_control.container import Container
from .win_control.form import Form
from .win_control.tab_page import TabPage
from .win_control.menu_bar import MenuBar
from .win_control.menu_item import MenuItem
from .win_control.table import Table
from .win_control.column import Column
from .win_control.row import Row
from .win_control.cell import Cell
from .win_control.raw_text import RawText
from .win_control.raw_text_block import RawTextBlock
from .win_control.raw_image import RawImage
from .win_control.grip import Grip
from .win_control.picture import Picture
from .web_element.body_tag import BodyTag
from .web_element.button_tag import ButtonTag
from .web_element.table_tag import TableTag
from .web_element.thead_tag import THeadTag
from .web_element.th_tag import ThTag
from .web_element.tbody_tag import TBodyTag
from .web_element.tr_tag import TrTag
from .web_element.td_tag import TdTag
from .web_element.input_tag import InputTag
from .web_element.a_tag import ATag
from .web_element.ul_tag import UlTag
from .web_element.li_tag import LiTag
from .web_element.div_tag import DivTag
from .web_element.em_tag import EmTag
from .web_element.i_tag import ITag
from .web_element.textarea_tag import TextAreaTag
from .web_element.web_document import WebDocument
from qturboframe.lib.utils.CustomError.custom_error import CustomError
from qturboframe.lib.ranorex.exceptions import CsClientError

def singleton(cls):
    # 创建一个字典用来保存被装饰类的实例对象 _instance = {}
    _instance = {}

    def _singleton(*args, **kwargs):
        # 判断这个类有没有创建过对象，没有新创建一个，有则返回之前创建的
        if cls not in _instance:
            _instance[cls] = cls(*args, **kwargs)
        return _instance[cls]

    return _singleton

@singleton
class CsClient(object):

    def __init__(self):
        pythoncom.CoInitialize()
        self.client = Dispatch('HsUIAutomation.WinClient.Client')
        try:
            self.client.Init
        except Exception as msg:
            print(msg)

    def export_settings(self, name):
        '''
        导出配置文件

        :Args:
         - name: 配置文件绝对路径
        '''
        self.client.ExportSettings(name)

    def import_settings(self, name):
        '''
        导入配置文件

        :Args:
         - name: 配置文件绝对路径
        '''
        self.client.ImportSettings(name)

    def open_application(self, path, args=None):
        '''
        打开应用程序

        :Args:
         - path: 应用程序的路径
         - args: 参数值

        :Returns:
            应用程序的进程ID
        '''
        if args:
            return self.client.OpenApplicationWithArgs(path, args)
        else:
            return self.client.OpenApplication(path)

    def get_window_by_attributes(self, attr_json):
        '''
        根据窗体最外层Form的属性获取窗体

        :Args:
         - json: json格式的字符串，按照属性为键，属性对应的值为值的形式组织

        :Returns:
            返回RWindow对象
        '''
        if isinstance(attr_json, dict):
            attr_json = json.dumps(attr_json)
        window = self.client.GetWindowbyAttributes(attr_json)
        return Window(window)

    def get_window_by_path(self, xpath):
        '''
        根据窗体的xpath，获取窗体

        :Args:
         - xpath: 窗体的xpath

        :Returns:
            返回RWindow对象
        '''
        window = self.client.GetWindowbyPath(xpath)
        return Window(window)

    def text(self, parent, path):
        '''
        获取一个RText对象

        :Args:
         - parent: 1、None，表示从窗体最顶层开始查找；2、某个控件对象，表示从此控件开始，往下层找
         - path: 1、path需要传入绝对路径；2、path传入的为相对此控件的相对路径

        :Returns:
            返回文本框对象
        '''
        text = self.client.Text(parent, path)
        return Text(text)

    def button(self, parent, path):
        '''
        获取一个RButton对象

        :Args:
         - parent: 1、None，表示从窗体最顶层开始查找；2、某个控件对象，表示从此控件开始，往下层找
         - path: 1、path需要传入绝对路径；2、path传入的为相对此控件的相对路径

        :Returns:
            返回按钮对象
        '''
        button = self.client.Button(parent, path)
        return Button(button)

    def context_menu(self, parent, path):
        '''
        获取一个RContextMenu对象

        :Args:
         - parent: 1、None，表示从窗体最顶层开始查找；2、某个控件对象，表示从此控件开始，往下层找
         - path: 1、path需要传入绝对路径；2、path传入的为相对此控件的相对路径

        :Returns:
            返回右键菜单对象
        '''
        context_menu = self.client.ContextMenu(parent, path)
        return ContextMenu(context_menu)

    def tool_bar(self, parent, path):
        '''
        获取一个RToolBar对象

        :Args:
         - parent: 1、None，表示从窗体最顶层开始查找；2、某个控件对象，表示从此控件开始，往下层找
         - path: 1、path需要传入绝对路径；2、path传入的为相对此控件的相对路径

        :Returns:
            返回工具栏对象
        '''
        toolbar = self.client.ToolBar(parent, path)
        return ToolBar(toolbar)

    def menu_item(self, parent, path):
        '''
        获取一个RMenuItem对象

        :Args:
         - parent: 1、None，表示从窗体最顶层开始查找；2、某个控件对象，表示从此控件开始，往下层找
         - path: 1、path需要传入绝对路径；2、path传入的为相对此控件的相对路径

        :Returns:
            返回菜单条目对象
        '''
        menu_item = self.client.MenuItem(parent, path)
        return MenuItem(menu_item)

    def unknown(self, parent, path):
        '''
        获取一个RUnknown对象

        :Args:
         - parent: 1、None，表示从窗体最顶层开始查找；2、某个控件对象，表示从此控件开始，往下层找
         - path: 1、path需要传入绝对路径；2、path传入的为相对此控件的相对路径

        :Returns:
            返回未知对象
        '''
        unknown = self.client.Unknown(parent, path)
        return Unknown(unknown)

    def combo_box(self, parent, path):
        '''
        获取一个RComboBox对象

        :Args:
         - parent: 1、None，表示从窗体最顶层开始查找；2、某个控件对象，表示从此控件开始，往下层找
         - path: 1、path需要传入绝对路径；2、path传入的为相对此控件的相对路径

        :Returns:
            返回下拉框对象
        '''
        combobox = self.client.ComboBox(parent, path)
        return ComboBox(combobox)

    def list(self, parent, path):
        '''
        获取一个RList对象

        :Args:
         - parent: 1、None，表示从窗体最顶层开始查找；2、某个控件对象，表示从此控件开始，往下层找
         - path: 1、path需要传入绝对路径；2、path传入的为相对此控件的相对路径

        :Returns:
            返回列表对象
        '''
        list = self.client.List(parent, path)
        return List(list)

    def list_item(self, parent, path):
        '''
        获取一个RListItem对象

        :Args:
         - parent: 1、None，表示从窗体最顶层开始查找；2、某个控件对象，表示从此控件开始，往下层找
         - path: 1、path需要传入绝对路径；2、path传入的为相对此控件的相对路径

        :Returns:
            返回列表条目对象
        '''
        list_item = self.client.ListItem(parent, path)
        return ListItem(list_item)

    def scroll_bar(self, parent, path):
        '''
        获取一个RScrollBar对象

        :Args:
         - parent: 1、None，表示从窗体最顶层开始查找；2、某个控件对象，表示从此控件开始，往下层找
         - path: 1、path需要传入绝对路径；2、path传入的为相对此控件的相对路径

        :Returns:
            返回滚动条对象
        '''
        scroll_bar = self.client.ScrollBar(parent, path)
        return ScrollBar(scroll_bar)

    def tree(self, parent, path):
        '''
        获取一个RTree对象

        :Args:
         - parent: 1、None，表示从窗体最顶层开始查找；2、某个控件对象，表示从此控件开始，往下层找
         - path: 1、path需要传入绝对路径；2、path传入的为相对此控件的相对路径

        :Returns:
            返回树形控件对象
        '''
        tree = self.client.Tree(parent, path)
        return Tree(tree)

    def tree_item(self, parent, path):
        '''
        获取一个RTreeItem对象

        :Args:
         - parent: 1、None，表示从窗体最顶层开始查找；2、某个控件对象，表示从此控件开始，往下层找
         - path: 1、path需要传入绝对路径；2、path传入的为相对此控件的相对路径

        :Returns:
            返回树形控件条目对象
        '''
        tree_item = self.client.TreeItem(parent, path)
        return TreeItem(tree_item)

    def checkbox(self, parent, path):
        '''
        获取一个RCheckBox对象

        :Args:
         - parent: 1、None，表示从窗体最顶层开始查找；2、某个控件对象，表示从此控件开始，往下层找
         - path: 1、path需要传入绝对路径；2、path传入的为相对此控件的相对路径

        :Returns:
            返回多选框对象
        '''
        checkbox = self.client.CheckBox(parent, path)
        return CheckBox(checkbox)

    def radio_button(self, parent, path):
        '''
        获取一个RRadioButton对象

        :Args:
         - parent: 1、None，表示从窗体最顶层开始查找；2、某个控件对象，表示从此控件开始，往下层找
         - path: 1、path需要传入绝对路径；2、path传入的为相对此控件的相对路径

        :Returns:
            返回单选按钮对象
        '''
        radio_button = self.client.RadioButton(parent, path)
        return RadioButton(radio_button)

    def container(self, parent, path):
        '''
        获取一个RContainer对象

        :Args:
         - parent: 1、None，表示从窗体最顶层开始查找；2、某个控件对象，表示从此控件开始，往下层找
         - path: 1、path需要传入绝对路径；2、path传入的为相对此控件的相对路径

        :Returns:
            返回容器对象
        '''
        container = self.client.Container(parent, path)
        return Container(container)

    def form(self, parent, path):
        '''
        获取一个RForm对象

        :Args:
         - parent: 1、None，表示从窗体最顶层开始查找；2、某个控件对象，表示从此控件开始，往下层找
         - path: 1、path需要传入绝对路径；2、path传入的为相对此控件的相对路径

        :Returns:
            返回窗体对象
        '''
        form = self.client.Form(parent, path)
        return Form(form)

    def tab_page(self, parent, path):
        '''
        获取一个RTabPage对象

        :Args:
         - parent: 1、None，表示从窗体最顶层开始查找；2、某个控件对象，表示从此控件开始，往下层找
         - path: 1、path需要传入绝对路径；2、path传入的为相对此控件的相对路径

        :Returns:
            返回标签页对象
        '''
        tab_page = self.client.TabPage(parent, path)
        return TabPage(tab_page)

    def menu_bar(self, parent, path):
        '''
        获取一个RMenuBar对象

        :Args:
         - parent: 1、None，表示从窗体最顶层开始查找；2、某个控件对象，表示从此控件开始，往下层找
         - path: 1、path需要传入绝对路径；2、path传入的为相对此控件的相对路径

        :Returns:
            返回菜单栏对象
        '''
        menu_bar = self.client.MenuBar(parent, path)
        return MenuBar(menu_bar)

    def table(self, parent, path):
        '''
        获取一个RTable对象

        :Args:
         - parent: 1、None，表示从窗体最顶层开始查找；2、某个控件对象，表示从此控件开始，往下层找
         - path: 1、path需要传入绝对路径；2、path传入的为相对此控件的相对路径

        :Returns:
            返回表格对象
        '''
        table = self.client.Table(parent, path)
        return Table(table)

    def row(self, parent, path):
        '''
        获取一个RRow对象

        :Args:
         - parent: 1、None，表示从窗体最顶层开始查找；2、某个控件对象，表示从此控件开始，往下层找
         - path: 1、path需要传入绝对路径；2、path传入的为相对此控件的相对路径

        :Returns:
            返回表格行对象
        '''
        row = self.client.Row(parent, path)
        return Row(row)

    def column(self, parent, path):
        '''
        获取一个RColumn对象

        :Args:
         - parent: 1、None，表示从窗体最顶层开始查找；2、某个控件对象，表示从此控件开始，往下层找
         - path: 1、path需要传入绝对路径；2、path传入的为相对此控件的相对路径

        :Returns:
            返回表格列对象
        '''
        column = self.client.Column(parent, path)
        return Column(column)

    def cell(self, parent, path):
        '''
        获取一个RCell对象

        :Args:
         - parent: 1、None，表示从窗体最顶层开始查找；2、某个控件对象，表示从此控件开始，往下层找
         - path: 1、path需要传入绝对路径；2、path传入的为相对此控件的相对路径

        :Returns:
            返回表格单元格对象
        '''
        cell = self.client.Cell(parent, path)
        return Cell(cell)

    def raw_text(self, parent, path):
        '''
        获取一个RRawText对象

        :Args:
         - parent: 1、None，表示从窗体最顶层开始查找；2、某个控件对象，表示从此控件开始，往下层找
         - path: 1、path需要传入绝对路径；2、path传入的为相对此控件的相对路径

        :Returns:
            返回RRawText对象
        '''
        raw_text = self.client.RawText(parent, path)
        return RawText(raw_text)

    def raw_text_block(self, parent, path):
        '''
        获取一个RRawTextBlock对象

        :Args:
         - parent: 1、None，表示从窗体最顶层开始查找；2、某个控件对象，表示从此控件开始，往下层找
         - path: 1、path需要传入绝对路径；2、path传入的为相对此控件的相对路径

        :Returns:
            返回RRawTextBlock对象
        '''
        raw_text_block = self.client.RawTextBlock(parent, path)
        return RawTextBlock(raw_text_block)

    def raw_image(self, parent, path):
        '''
        获取一个RRawImage对象

        :Args:
         - parent: 1、None，表示从窗体最顶层开始查找；2、某个控件对象，表示从此控件开始，往下层找
         - path: 1、path需要传入绝对路径；2、path传入的为相对此控件的相对路径

        :Returns:
            返回RRawImage对象
        '''
        raw_image = self.client.RawImage(parent, path)
        return RawImage(raw_image)

    def grip(self, parent, path):
        '''
        获取一个RGrip对象

        :Args:
         - parent: 1、None，表示从窗体最顶层开始查找；2、某个控件对象，表示从此控件开始，往下层找
         - path: 1、path需要传入绝对路径；2、path传入的为相对此控件的相对路径

        :Returns:
            返回RGrip对象
        '''
        grip = self.client.Grip(parent, path)
        return Grip(grip)

    def indicator(self, parent, path):
        '''
        获取一个RIndicator对象

        :Args:
         - parent: 1、None，表示从窗体最顶层开始查找；2、某个控件对象，表示从此控件开始，往下层找
         - path: 1、path需要传入绝对路径；2、path传入的为相对此控件的相对路径

        :Returns:
            返回滑块对象
        '''
        indicator = self.client.Indicator(parent, path)
        return Indicator(indicator)

    def picture(self, parent, path):
        '''
        获取一个RPicture对象

        :Args:
         - parent: 1、None，表示从窗体最顶层开始查找；2、某个控件对象，表示从此控件开始，往下层找
         - path: 1、path需要传入绝对路径；2、path传入的为相对此控件的相对路径

        :Returns:
            返回图片对象
        '''
        picture = self.client.Picture(parent, path)
        return Picture(picture)

    def div_tag(self, parent, path):
        '''
        获取一个DivTag对象

        :Args:
         - parent: 1、None，表示从窗体最顶层开始查找；2、某个控件对象，表示从此控件开始，往下层找
         - path: 1、path需要传入绝对路径；2、path传入的为相对此控件的相对路径

        :Returns:
            返回div元素对象
        '''
        div_tag = self.client.DivTag(parent, path)
        return DivTag(div_tag)

    def body_tag(self, parent, path):
        '''
        获取一个BodyTag对象

        :Args:
         - parent: 1、None，表示从窗体最顶层开始查找；2、某个控件对象，表示从此控件开始，往下层找
         - path: 1、path需要传入绝对路径；2、path传入的为相对此控件的相对路径

        :Returns:
            返回body元素对象
        '''
        body_tag = self.client.BodyTag(parent, path)
        return BodyTag(body_tag)

    def tbody_tag(self, parent, path):
        '''
        获取一个TBodyTag对象

        :Args:
         - parent: 1、None，表示从窗体最顶层开始查找；2、某个控件对象，表示从此控件开始，往下层找
         - path: 1、path需要传入绝对路径；2、path传入的为相对此控件的相对路径

        :Returns:
            返回tbody元素对象
        '''
        tbody_tag = self.client.TBodyTag(parent, path)
        return TBodyTag(tbody_tag)

    def button_tag(self, parent, path):
        '''
        获取一个ButtonTag对象

        :Args:
         - parent: 1、None，表示从窗体最顶层开始查找；2、某个控件对象，表示从此控件开始，往下层找
         - path: 1、path需要传入绝对路径；2、path传入的为相对此控件的相对路径

        :Returns:
            返回按钮元素对象
        '''
        button_tag = self.client.ButtonTag(parent, path)
        return ButtonTag(button_tag)

    def table_tag(self, parent, path):
        '''
        获取一个TableTag对象

        :Args:
         - parent: 1、None，表示从窗体最顶层开始查找；2、某个控件对象，表示从此控件开始，往下层找
         - path: 1、path需要传入绝对路径；2、path传入的为相对此控件的相对路径

        :Returns:
            返回表格元素对象
        '''
        table_tag = self.client.TableTag(parent, path)
        return TableTag(table_tag)

    def td_tag(self, parent, path):
        '''
        获取一个TdTag对象

        :Args:
         - parent: 1、None，表示从窗体最顶层开始查找；2、某个控件对象，表示从此控件开始，往下层找
         - path: 1、path需要传入绝对路径；2、path传入的为相对此控件的相对路径

        :Returns:
            返回表格单元格元素对象
        '''
        td_tag = self.client.TdTag(parent, path)
        return TdTag(td_tag)

    def tr_tag(self, parent, path):
        '''
        获取一个TrTag对象

        :Args:
         - parent: 1、None，表示从窗体最顶层开始查找；2、某个控件对象，表示从此控件开始，往下层找
         - path: 1、path需要传入绝对路径；2、path传入的为相对此控件的相对路径

        :Returns:
            返回表格行元素对象
        '''
        tr_tag = self.client.TrTag(parent, path)
        return TrTag(tr_tag)

    def input_tag(self, parent, path):
        '''
        获取一个InputTag对象

        :Args:
         - parent: 1、None，表示从窗体最顶层开始查找；2、某个控件对象，表示从此控件开始，往下层找
         - path: 1、path需要传入绝对路径；2、path传入的为相对此控件的相对路径

        :Returns:
            返回input元素对象
        '''
        input_tag = self.client.InputTag(parent, path)
        return InputTag(input_tag)

    def a_tag(self, parent, path):
        '''
        获取一个ATag对象

        :Args:
         - parent: 1、None，表示从窗体最顶层开始查找；2、某个控件对象，表示从此控件开始，往下层找
         - path: 1、path需要传入绝对路径；2、path传入的为相对此控件的相对路径

        :Returns:
            返回a元素对象
        '''
        a_tag = self.client.ATag(parent, path)
        return ATag(a_tag)

    def li_tag(self, parent, path):
        '''
        获取一个LiTag对象

        :Args:
         - parent: 1、None，表示从窗体最顶层开始查找；2、某个控件对象，表示从此控件开始，往下层找
         - path: 1、path需要传入绝对路径；2、path传入的为相对此控件的相对路径

        :Returns:
            返回li元素对象
        '''
        li_tag = self.client.LiTag(parent, path)
        return LiTag(li_tag)

    def web_document(self, parent, path):
        '''
        获取一个WebDocument对象

        :Args:
         - parent: 1、None，表示从窗体最顶层开始查找；2、某个控件对象，表示从此控件开始，往下层找
         - path: 1、path需要传入绝对路径；2、path传入的为相对此控件的相对路径

        :Returns:
            返回webdocument元素对象
        '''
        web_document = self.client.WebDocument(parent, path)
        return WebDocument(web_document)

    def get_web_document_by_path(self, path):
        '''
        获取一个WebDocument对象

        :Args:
         - path: path绝对路径

        :Returns:
            返回webdocument元素对象
        '''
        web_document = self.client.WebDocument(None, path)
        return WebDocument(web_document)

    def em_tag(self, parent, path):
        '''
        获取一个EmTag对象

        :Args:
         - parent: 1、None，表示从窗体最顶层开始查找；2、某个控件对象，表示从此控件开始，往下层找
         - path: 1、path需要传入绝对路径；2、path传入的为相对此控件的相对路径

        :Returns:
            返回em元素对象
        '''
        em = self.client.EmTag(parent, path)
        return EmTag(em)

    def textarea_tag(self, parent, path):
        '''
        获取一个TextAreaTag对象

        :Args:
         - parent: 1、None，表示从窗体最顶层开始查找；2、某个控件对象，表示从此控件开始，往下层找
         - path: 1、path需要传入绝对路径；2、path传入的为相对此控件的相对路径

        :Returns:
            返回textarea元素对象
        '''
        textarea = self.client.TextAreaTag(parent, path)
        return TextAreaTag(textarea)

    def thead_tag(self, parent, path):
        '''
        获取一个THeadTag对象

        :Args:
         - parent: 1、None，表示从窗体最顶层开始查找；2、某个控件对象，表示从此控件开始，往下层找
         - path: 1、path需要传入绝对路径；2、path传入的为相对此控件的相对路径

        :Returns:
            返回thead元素对象
        '''
        thead = self.client.THeadTag(parent, path)
        return THeadTag(thead)

    def th_tag(self, parent, path):
        '''
        获取一个ThTag对象

        :Args:
         - parent: 1、None，表示从窗体最顶层开始查找；2、某个控件对象，表示从此控件开始，往下层找
         - path: 1、path需要传入绝对路径；2、path传入的为相对此控件的相对路径

        :Returns:
            返回th元素对象
        '''
        th = self.client.ThTag(parent, path)
        return ThTag(th)

    def ul_tag(self, parent, path):
        '''
        获取一个UlTag对象

        :Args:
         - parent: 1、None，表示从窗体最顶层开始查找；2、某个控件对象，表示从此控件开始，往下层找
         - path: 1、path需要传入绝对路径；2、path传入的为相对此控件的相对路径

        :Returns:
            返回ul元素对象
        '''
        ul = self.client.UlTag(parent, path)
        return UlTag(ul)

    def i_tag(self, parent, path):
        '''
        获取一个ITag对象

        :Args:
         - parent: 1、None，表示从窗体最顶层开始查找；2、某个控件对象，表示从此控件开始，往下层找
         - path: 1、path需要传入绝对路径；2、path传入的为相对此控件的相对路径

        :Returns:
            返回i元素对象
        '''
        i = self.client.ITag(parent, path)
        return ITag(i)

    def find_control_by_path(self, path, time=10000):
        '''
        获取控件对象，无需明确时何类型的对象

        :Args:
         - path: Ranorex中元素的绝对路径
         - time: 查找的超时时间，默认为10S

        :Returns:
            返回控件对象
        '''
        try:
            control = self.client.FindControlByPath(path, time)
            return Control(control)
        except pywintypes.com_error as e:
            if 'Invalid RxPath' in str(e) or "Parsing RxPath" in str(e):
                message = "{}为无效的XPATH，请检查".format(path)
                raise CsClientError(CustomError.Error_EQ03001001_XPath_Invalid, extra_msg=message)
            elif 'No element found for path' in str(e):
                message = "在{}秒内没有找到XPath为{}的控件".format(time / 1000, path)
                # logger.warn(message)
                raise CsClientError(CustomError.Error_EQ03001000_Cannot_Find_Element, extra_msg=message)
            else:
                raise CsClientError(str(e))


    def is_element_exist(self, path, time=2000):
        '''
        判断控件是否存在

        :Args:
         - path: Ranorex中元素的绝对路径
         - time: 查找的超时时间，默认为2S

        :Returns:
            返回True表示元素存在，否则表示元素不存在
        '''
        try:
            return self.client.IsElementExist(path, time)
        except pywintypes.com_error as e:
            if 'Invalid RxPath' in str(e) or "Parsing RxPath" in str(e):
                message = "{}为无效的XPATH，请检查".format(path)
                raise CsClientError(CustomError.Error_EQ03001001_XPath_Invalid, extra_msg=message)
            else:
                # self.get_memory()
                raise CsClientError(str(e))

    def capture_desktop_image(self, x, y, width, height, filename):
        '''
        抓取桌面图片

        :Args:
         - x: 原点的x轴坐标
         - y: 原点的y轴坐标
         - width: 控件的宽度
         - height: 控件的高度
         - filename: 保存下来的文件的路径
        '''
        self.client.CaptureDesktopImage(x, y, width, height, filename)

